﻿using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Actya.Core.Infrastructure.Events;
using Actya.Core.SiteStructure;
using Actya.Core.SiteStructure.Events;
using Actya.Web.Infrastructure;
using Autofac;
using NUnit.Framework;

namespace Actya.IntegrationTests
{
	[TestFixture]	
	public class EventPublisherTests
	{
		[Test]
		public void RegisterEventPublisherWithMultipleListeners()
		{
			var builder = new ContainerBuilder();
			builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
				.Where(t => t.Name.EndsWith("Handler"))
				.AsImplementedInterfaces()
				.InstancePerLifetimeScope();
			builder.RegisterType<EventPublisher>().As<IEventPublisher>()
				.InstancePerLifetimeScope();
			var container = builder.Build();

			var eventPublisher = container.Resolve<IEventPublisher>();
			var testHandler = container.Resolve<ITestHandler>();
			Assert.That(testHandler.Text, Is.EqualTo("DEFAULT"));
			var testForMultipleEventsHandler = container.Resolve<ITestForMultipleEventsHandler>();
			Assert.That(testForMultipleEventsHandler.Text, Is.EqualTo("DEFAULT"));

			var site = new Site {Name = "SITE_UPDATED"};
			var siteUpdated = new SiteUpdated(site);
			eventPublisher.Publish(siteUpdated);

			Assert.That(testHandler.Text, Is.EqualTo(site.Name));
			Assert.That(testForMultipleEventsHandler.Text, Is.EqualTo(site.Name));
		}

		[Test]
		public void RegisterEventPublisherWithMultipleEvents()
		{
			var builder = new ContainerBuilder();
			builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
				.Where(t => t.Name.EndsWith("Handler"))
				.AsImplementedInterfaces()
				.InstancePerLifetimeScope();
			builder.RegisterType<EventPublisher>().As<IEventPublisher>()
				.InstancePerLifetimeScope();
			var container = builder.Build();

			var eventPublisher = container.Resolve<IEventPublisher>();
			var testHandler = container.Resolve<ITestHandler>();
			Assert.That(testHandler.Text, Is.EqualTo("DEFAULT"));
			var testForMultipleEventsHandler = container.Resolve<ITestForMultipleEventsHandler>();
			Assert.That(testForMultipleEventsHandler.Text, Is.EqualTo("DEFAULT"));


			var site = new Site { Name = "SITE_UPDATED" };
			var siteUpdated = new SiteUpdated(site);
			eventPublisher.Publish(siteUpdated);

			Assert.That(testHandler.Text, Is.EqualTo(site.Name));
			Assert.That(testForMultipleEventsHandler.Text, Is.EqualTo(site.Name));

			var testPayload = new TestPayload();
			testPayload.DummyText = "DUMMY";
			eventPublisher.Publish(testPayload);

			Assert.That(testHandler.Text, Is.EqualTo(site.Name));
			Assert.That(testForMultipleEventsHandler.Text, Is.EqualTo("DUMMY"));
		}

		[Test]
		public void RegisterSitemapProviderInstanceAsIHandle()
		{
			// Arrange
			var sitemapProvider = new CmsSiteMapProvider();
			var builder = new ContainerBuilder();
			builder.RegisterInstance(sitemapProvider).AsImplementedInterfaces().ExternallyOwned();

			// Act
			var container = builder.Build();

			// Assert
			var handlers = container.Resolve<IEnumerable<IEventListener>>();
			Assert.That(handlers.Count(), Is.EqualTo(1));
			Assert.That(handlers.First(), Is.EqualTo(sitemapProvider));
		}

		#region TestClasses and Interfaces

		public interface ITestHandler
		{
			string Text { get; }
		}

		public class TestHandler : ITestHandler, IEventListener<SiteUpdated>
		{
			public string Text { get; private set; }

			public TestHandler()
			{
				this.Text = "DEFAULT";
			}

			public void Handle(SiteUpdated payload)
			{
				this.Text = payload.Site.Name;
			}
		}

		public interface ITestForMultipleEventsHandler
		{
			string Text { get; }
		}

		public class TestForMultipleEventsHandler : ITestForMultipleEventsHandler, IEventListener<SiteUpdated>, IEventListener<TestPayload>
		{
			public string Text { get; private set; }
			public TestForMultipleEventsHandler()
			{
				this.Text = "DEFAULT";
			}

			public void Handle(SiteUpdated payload)
			{
				this.Text = payload.Site.Name;
			}

			public void Handle(TestPayload payload)
			{
				this.Text = payload.DummyText;
			}
		}

		public class TestPayload
		{
			public string DummyText { get; set; }
		}

		#endregion
	}
}
